#pragma once

#include "except.hh"
#include <csignal>
#include <jsoncpp/json/json.h>
#include <memory>
#include <spdlog/spdlog.h>
#include <string>

namespace WordMemory {
    struct JsonUtil {
        static std::string serialize(const Json::Value& root) {
            std::unique_ptr<Json::StreamWriter> sw(
                Json::StreamWriterBuilder().newStreamWriter());
            std::stringstream res;
            if (sw->write(root, &res) != 0)
                throw UtilExcept("serialize write error");
            return res.str();
        }

        static Json::Value deserialize(const std::string& str) {
            std::unique_ptr<Json::CharReader> cr(
                Json::CharReaderBuilder().newCharReader());

            Json::Value root;
            std::string errs;
            if (!cr->parse(str.c_str(), str.c_str() + str.size(), &root, &errs))
                throw UtilExcept("deserialize parse error " + errs);
            return root;
        }
    };

    struct FileUtil {
        static std::string read(const std::string& path, size_t pos = 0,
            size_t len = std::string::npos) {
            std::ifstream ifs(path);
            if (!ifs.is_open())
                throw UtilExcept("read_file error " + path);

            std::string res;

            if (len == std::string::npos)
                len = size(path);

            res.resize(len);

            ifs.seekg(pos, std::ios::beg);
            ifs.read(&res[0], len);

            return res;
        }

        static int64_t size(const std::string& path) {
            struct stat st;
            if (stat(path.c_str(), &st) == -1)
                throw UtilExcept("file_size error " + path);
            return st.st_size;
        }
    };

    struct SignalUtil
    {
        static std::string getName(int signo)
        {
            switch (signo) {
            case SIGHUP:    return "SIGHUP";
            case SIGINT:    return "SIGINT";
            case SIGQUIT:   return "SIGQUIT";
            case SIGILL:    return "SIGILL";
            case SIGTRAP:   return "SIGTRAP";
            case SIGABRT:   return "SIGABRT";
            case SIGBUS:    return "SIGBUS";
            case SIGFPE:    return "SIGFPE";
            case SIGKILL:   return "SIGKILL";
            case SIGUSR1:   return "SIGUSR1";
            case SIGSEGV:   return "SIGSEGV";
            case SIGUSR2:   return "SIGUSR2";
            case SIGPIPE:   return "SIGPIPE";
            case SIGALRM:   return "SIGALRM";
            case SIGTERM:   return "SIGTERM";
            case SIGSTKFLT: return "SIGSTKFLT";
            case SIGCHLD:   return "SIGCHLD";
            case SIGCONT:   return "SIGCONT";
            case SIGSTOP:   return "SIGSTOP";
            case SIGTSTP:   return "SIGTSTP";
            case SIGTTIN:   return "SIGTTIN";
            case SIGTTOU:   return "SIGTTOU";
            case SIGURG:    return "SIGURG";
            case SIGXCPU:   return "SIGXCPU";
            case SIGXFSZ:   return "SIGXFSZ";
            case SIGVTALRM: return "SIGVTALRM";
            case SIGPROF:   return "SIGPROF";
            case SIGWINCH:  return "SIGWINCH";
            case SIGIO:     return "SIGIO";
            case SIGPWR:    return "SIGPWR";
            case SIGSYS:    return "SIGSYS";
            default:        return "UNKNOWN";
            }
        }

        static void setupHandlers()
        {
            for (int signo = 1; signo < 32; ++signo)
            {
                if (signal(signo,[](int sigrev) {
                        if (sigrev == SIGINT) {
                            spdlog::critical("Received signal: {} ({}), server stopped gracefully.", sigrev, getName(sigrev));
                            exit(sigrev);
                        }

                        spdlog::warn("Ignored signal received: {} ({})", sigrev, getName(sigrev));

                    }) == SIG_ERR) {

                    if (signo == SIGKILL || signo == SIGSTOP) {
                        continue;
                    }
                    else {
                        spdlog::error("Failed to set signal handler for {} ({})", signo, getName(signo));
                        throw UtilExcept("Failed to set signal handler");
                    }
                }
            }
            spdlog::info("Signal handlers setup completed");
        }
    };
} // namespace WordMemory
